import {
  Controller,
  Param,
  Get,
  Post,
  Put,
  Delete,
  UseInterceptors,
  Body,
  Request,
  Query,
} from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
import { WebResult } from "src/common/web.result";
import { LoginInterceptor } from "src/user/interceptor/login.interceptor";
import { ArticlePublishDTO } from "./dto/article-publish.dto";

import { FastifyRequest } from "fastify";
import { Article } from "./interface/article";
import { ArticleContentDataType } from "./enum/article-content-data";
import { ArticleUpdateDTO } from "./dto/article-update.dto";
import {
  ArticleDTODealError,
  DealtArticleDTO,
} from "./interface/article-dealt";

import { InformationService } from "src/common/information.module";
import { UserTokenService } from "src/user/token.service";
import { ArticleService } from "./article.service";

const pageSize = 10;

@Controller("article")
export class ArticleController {
  constructor(
    private readonly prisma: PrismaClient,
    private readonly informationService: InformationService,
    private readonly userTokenService: UserTokenService,
    private readonly articleService: ArticleService,
  ) {}

  @Get("/sort/all")
  async getSortAll() {
    const sorts = await this.prisma.article_sort.findMany();
    return WebResult.success(sorts);
  }
  @Get("/sort/page/:page")
  async getSortByPage(@Param("page") page: string) {
    const selectPage = parseInt(page);
    if (isNaN(selectPage)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    const sorts = await this.prisma.article_sort.findMany({
      take: pageSize,
      skip: (selectPage - 1) * pageSize,
    });
    return WebResult.success(sorts);
  }

  /**
   * 分页获取 article
   * @param page 页数
   * @returns article[]
   */
  @Get("/page/:page")
  async getArticleInformationByPage(
    @Param("page") page: string,
    @Query("size") pageSize: number = 10,
    @Query("content") content: boolean = true,
    @Query("richContent") richContent: boolean = false,
  ) {
    try {
      let selectPage = parseInt(page);
      if (isNaN(selectPage)) {
        return WebResult.failure(undefined, "page is not a number");
      }
      if (selectPage <= 0) selectPage = 1;
      const arr = await this.articleService.getArticleByPage(
        selectPage,
        pageSize,
        content,
        richContent,
        undefined,
      );
      return WebResult.success(arr);
    } catch (e) {
      console.error(e);
      return WebResult.failure();
    }
  }

  // 以用户信息获取 article information 带 content
  @Get(["/user/id/:id", "/publisher/id/:id"])
  async getArticleInformationByPublisherID(
    @Param("id") publisherID: string | number,
  ) {
    if (typeof publisherID !== "number") publisherID = parseInt(publisherID);
    if (isNaN(publisherID)) {
      return WebResult.failure(undefined, "publisherID is not a number");
    }
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        content: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        publisher_id: publisherID,
      },
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = element.sort_name;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }

    return WebResult.success(infoArray);
  }
  @Get(["/user/id/:id/page/:page", "/publisher/id/:id/page/:page"])
  async getArticleInformationByPublisherIDAndPage(
    @Param("id") publisherID: string | number,
    @Param("page") page: string | number,
  ) {
    if (typeof publisherID !== "number") publisherID = parseInt(publisherID);
    if (typeof page !== "number") page = parseInt(page);
    if (isNaN(publisherID)) {
      return WebResult.failure(undefined, "publisherID is not a number");
    }
    if (isNaN(page)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        content: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        publisher_id: publisherID,
      },
      take: pageSize,
      skip: (page - 1) * pageSize,
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = element.sort_name;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }

    return WebResult.success(infoArray);
  }

  @Get(["/user/username/:username", "/publisher/username/:username"])
  async getArticleInformationByPublisherUsername(
    @Param("username") username: string,
  ) {
    const publisher = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        username,
      },
    });
    if (!publisher) return WebResult.failure();
    const publisherID = publisher.id;

    return await this.getArticleInformationByPublisherID(publisherID);
  }
  @Get([
    "/user/username/:username/page/:page",
    "/publisher/username/:username/page/:page",
  ])
  async getArticleInformationByPublisherUsernameAndPage(
    @Param("username") username: string,
    @Param("page") page: string | number,
  ) {
    if (typeof page !== "number") page = parseInt(page);
    if (isNaN(page)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    const publisher = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        username,
      },
    });
    if (!publisher) return WebResult.failure();
    const publisherID = publisher.id;

    return await this.getArticleInformationByPublisherIDAndPage(
      publisherID,
      page,
    );
  }

  @Get(["/user/nickname/:nickname", "/publisher/nickname/:nickname"])
  async getArticleInformationByPublisherNickname(
    @Param("nickname") nickname: string,
  ) {
    const publisher = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        nickname,
      },
    });
    if (!publisher) return WebResult.failure();
    const publisherID = publisher.id;

    return await this.getArticleInformationByPublisherID(publisherID);
  }
  @Get([
    "/user/nickname/:nickname/page/:page",
    "/publisher/nickname/:nickname/page/:page",
  ])
  async getArticleInformationByPublisherNicknameAndPage(
    @Param("nickname") nickname: string,
    @Param("page") page: string | number,
  ) {
    if (typeof page !== "number") page = parseInt(page);
    if (isNaN(page)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    const publisher = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        nickname,
      },
    });
    if (!publisher) return WebResult.failure();
    const publisherID = publisher.id;

    return await this.getArticleInformationByPublisherIDAndPage(
      publisherID,
      page,
    );
  }

  /**
   * 分类名或分类id 获取 article information
   * @returns articlesInformation
   */
  @Get("/sort/:sort")
  async getArticleInformationBySort(@Param("sort") sort: string) {
    let sortID = parseInt(sort);
    let sortName = sort;
    if (isNaN(sortID)) {
      sortID = await this.articleService.getSortIDByName(sort);
    } else {
      sortName = await this.articleService.getSortNameByID(sortID);
    }
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        content: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        sort_id: sortID,
      },
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = sortName;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }
    return WebResult.success(infoArray);
  }

  /**
   * 分类名或分类id 分页获取 article information
   * @returns articlesInformation
   */
  @Get("/sort/:sort/page/:page")
  async getArticleInformationBySortAndPage(
    @Param("sort") sort: string,
    @Param("page") page: string | number,
  ) {
    if (typeof page !== "number") page = parseInt(page);
    if (isNaN(page)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    let sortID = parseInt(sort);
    let sortName = sort;
    if (isNaN(sortID)) {
      sortID = await this.articleService.getSortIDByName(sort);
    } else {
      sortName = await this.articleService.getSortNameByID(sortID);
    }
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        content: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        sort_id: sortID,
      },
      take: pageSize,
      skip: (page - 1) * pageSize,
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = sortName;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }
    return WebResult.success(infoArray);
  }

  // 搜索，不带 content
  @Get("/search/title/:title")
  async searchArticleInformationByTitle(@Param("title") title: string) {
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        title: {
          contains: title,
        },
      },
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = element.sort_name;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }

    return WebResult.success(infoArray);
  }
  @Get("/search/title/:title/page/:page")
  async searchArticleInformationByTitleAndPage(
    @Param("title") title: string,
    @Param("page") page: string | number,
  ) {
    if (typeof page !== "number") page = parseInt(page);
    if (isNaN(page)) {
      return WebResult.failure(undefined, "page is not a number");
    }
    const infoArray: any[] = await this.prisma.article.findMany({
      select: {
        id: true,
        author: true,
        author_home_page: true,
        cover: true,
        title: true,
        date: true,
        sort_id: true,
      },
      orderBy: {
        update_time: "desc",
      },
      where: {
        title: {
          contains: title,
        },
      },
      take: pageSize,
      skip: (page - 1) * pageSize,
    });
    for (const element of infoArray) {
      element.sort_name = await this.articleService.getSortNameByID(
        element.sort_id,
      );
      element.sort = element.sort_name;
      element.date = new Date(element.date);
      element.date = `${element.date.getFullYear()}/${element.date.getMonth() + 1}/${element.date.getDate()}`;
    }

    return WebResult.success(infoArray);
  }

  // 计数
  @Get("/count")
  async getArticleCount() {
    const count = await this.prisma.article.count();
    return WebResult.success(count);
  }
  @Get(["/count/user/id/:id", "/count/publisher/id/:id"])
  async getArticleCountByPublisherID(
    @Param("id") publisherID: string | number,
  ) {
    if (typeof publisherID !== "number") publisherID = parseInt(publisherID);
    if (isNaN(publisherID)) {
      return WebResult.failure(undefined, "publisherID is not a number");
    }
    const count = await this.prisma.article.count({
      where: {
        publisher_id: publisherID,
      },
    });
    return WebResult.success(count);
  }
  @Get([
    "/count/user/username/:username",
    "/count/publisher/username/:username",
  ])
  async getArticleCountByPublisherUsername(
    @Param("username") username: string,
  ) {
    const publisher = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        username,
      },
    });
    if (!publisher) return WebResult.failure();
    const publisherID = publisher.id;

    return await this.getArticleCountByPublisherID(publisherID);
  }

  // 文章浏览量
  @Get(["/view/:id", "/view/id/:id"])
  async getArticleViewCountByID(@Param("id") id: string) {
    const articleID = parseInt(id);
    if (isNaN(articleID)) {
      return WebResult.failure(undefined, "id is not a number");
    }
    const view = await this.prisma.article_view_count.findUnique({
      select: {
        count: true,
      },
      where: {
        article_id: articleID,
      },
    });
    if (!view) {
      this.prisma.article_view_count
        .create({
          data: {
            article_id: articleID,
            count: 0,
          },
        })
        .then();
      return WebResult.failure();
    }
    return WebResult.success(view.count);
  }

  // 获取文章
  @Get(["/:id", "/id/:id"])
  async getArticleByID(
    @Request() request: FastifyRequest,
    @Param("id") id: string,
  ) {
    const article = await this.articleService.getArticleByID(id, {
      getRichContent: true,
    });
    if (!article) return WebResult.failure(article);

    // 创建浏览记录与更新浏览量
    const userIP = this.informationService.getIP(request);
    const userArea = this.informationService.getRegion(userIP);
    const uaInfo = this.informationService.getUAInformation(request);
    this.prisma.article_view
      .create({
        data: {
          article_id: article.id,
          ip: userIP,
          area: userArea,
          os: uaInfo.os,
          browser: uaInfo.browser,
        },
      })
      .then();

    let view = await this.prisma.article_view_count.findUnique({
      where: {
        article_id: article.id,
      },
    });
    if (!view) {
      view = await this.prisma.article_view_count.create({
        data: {
          article_id: article.id,
          count: 0,
        },
      });
    }
    const addArticleView = async () => {
      await this.prisma.article_view_count.update({
        data: {
          count: ++view.count,
          last_view_ip: userIP,
          update_time: new Date(),
        },
        where: {
          article_id: article.id,
        },
      });
    };
    if (userIP !== view.last_view_ip) {
      addArticleView();
    } else {
      const elapseMS = new Date().getTime() - view.update_time.getTime();
      const elapseHour = elapseMS / 1000 / 60 / 60;
      // 过了一小时
      if (elapseHour >= 1) {
        addArticleView();
      }
    }

    article.view = view.count;
    return WebResult.success(article);
  }
  // 获取文章，但是 content 为 markdown
  @Get(["/:id/markdown", "/id/:id/markdown"])
  async getArticleMarkdownContentByID(@Param("id") id: string) {
    const article = await this.articleService.getArticleByID(id, {
      getRichContent: true,
      contentType: ArticleContentDataType.Markdown,
    });
    if (!article) return WebResult.failure();
    return WebResult.success(article);
  }
  @Get(["/information/:id", "/information/id/:id"])
  async getArticleInformationByID(@Param("id") id: string) {
    const info = await this.articleService.getArticleByID(id, {
      getRichContent: false,
    });
    if (!info) return WebResult.failure();
    return WebResult.success(info);
  }

  @UseInterceptors(LoginInterceptor)
  @Post(["/", "/publish"])
  async publish(
    @Request() request: FastifyRequest,
    @Body() articlePublishDTO: ArticlePublishDTO,
  ) {
    // publisher id
    const uid = this.userTokenService.getUserIDFromRequest(request);

    let dealtDTO: DealtArticleDTO;
    let article: Article;
    try {
      dealtDTO = await this.articleService.dealArticleDTO(articlePublishDTO);

      article = await this.prisma.article.create({
        data: {
          publisher_id: uid,
          sort_id: dealtDTO.sort_id,
          cover: dealtDTO.cover,
          title: dealtDTO.title,
          author: dealtDTO.author,
          author_home_page: dealtDTO.author_home_page,
          date: dealtDTO.date,
          content: dealtDTO.textContent,
        },
      });
      if (!article) {
        return WebResult.failure(undefined, "发布失败，内部错误");
      }
    } catch (e) {
      console.error(e);
      if (e instanceof ArticleDTODealError) {
        return WebResult.failure(undefined, e.message);
      }
      return WebResult.failure(undefined, "发布失败，内部错误");
    }

    try {
      // 存储本地
      // xml
      if (dealtDTO.content_type === "xml" || dealtDTO.content_type === "XML") {
        // 存储json
        this.articleService.saveArticleContentDataByXML(
          {
            publisher_id: uid,
            id: article.id,
          },
          dealtDTO.content,
        );
      }
      // markdown
      else if (
        dealtDTO.content_type === "markdown" ||
        dealtDTO.content_type === "MARKDOWN"
      ) {
        // 存储 markdown
        this.articleService.saveArticleContentDataByMarkdown(
          {
            publisher_id: uid,
            id: article.id,
          },
          dealtDTO.content,
        );
      }

      await this.prisma.$transaction([
        // 归档
        this.prisma.article_archive.create({
          data: {
            name: new Date(article.date).getFullYear().toString(),
            article_id: article.id,
          },
        }),
        // 浏览记录数据
        this.prisma.article_view_count.create({
          data: {
            article_id: article.id,
            count: 0,
          },
        }),
      ]);

      // 发布成功
      return WebResult.success(undefined, "发布成功");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "发布失败，数据存储错误");
    }
  }

  @UseInterceptors(LoginInterceptor)
  @Put(["/", "/update"])
  async update(
    @Request() request: FastifyRequest,
    @Body() articleUpdateDTO: ArticleUpdateDTO,
  ) {
    const uid = this.userTokenService.getUserIDFromRequest(request);
    if (!articleUpdateDTO.id) {
      return WebResult.failure(undefined, "缺少必要参数id");
    }

    try {
      const article = await this.prisma.article.findUnique({
        select: {
          publisher_id: true,
          sort_id: true,
          author: true,
          author_home_page: true,
          cover: true,
          title: true,
          date: true,
        },
        where: {
          id: articleUpdateDTO.id,
        },
      });
      if (!article) {
        return WebResult.failure(undefined, "不存在的文章");
      }
      if (article.publisher_id !== uid) {
        return WebResult.failure(undefined, "没有权限");
      }

      const dealtDTO: DealtArticleDTO =
        await this.articleService.dealArticleDTO(articleUpdateDTO);
      // console.log(dealtDTO);

      await this.prisma.article.update({
        where: {
          id: articleUpdateDTO.id,
        },
        data: {
          sort_id: dealtDTO.sort_id || article.sort_id,
          author: dealtDTO.author || article.author,
          author_home_page:
            dealtDTO.author_home_page || article.author_home_page,
          cover: dealtDTO.cover || article.cover,
          title: dealtDTO.title || article.title,
          date: dealtDTO.date || article.date,
          content: dealtDTO.textContent,
        },
      });

      // 更新 ArticleContentData
      this.articleService.saveArticleContentData(
        {
          publisher_id: uid,
          id: articleUpdateDTO.id,
        },
        dealtDTO.content_type,
        dealtDTO.content,
      );
      return WebResult.success(undefined, "更新成功");
    } catch (e) {
      console.error(e);
      if (e instanceof ArticleDTODealError) {
        return WebResult.failure(undefined, e.message);
      }
      return WebResult.failure(undefined, "内部错误");
    }
  }

  @UseInterceptors(LoginInterceptor)
  @Put(["/:id", "/update/:id"])
  async updateByPathID(
    @Request() request: FastifyRequest,
    @Param("id") id: string,
    @Body() articleUpdateDTO: ArticleUpdateDTO,
  ) {
    const articleID = parseInt(id);
    if (isNaN(articleID)) {
      return WebResult.failure(undefined, "id is not a number");
    }
    articleUpdateDTO.id = articleID;
    return await this.update(request, articleUpdateDTO);
  }

  @UseInterceptors(LoginInterceptor)
  @Delete(["/:id", "/delete/:id"])
  async deleteByPathID(
    @Request() request: FastifyRequest,
    @Param("id") id: string,
  ) {
    const articleID = parseInt(id);
    if (isNaN(articleID)) {
      return WebResult.failure(undefined, "article id must be a number");
    }
    const uid = this.userTokenService.getUserIDFromRequest(request);
    const article = await this.prisma.article.findUnique({
      select: {
        sort_id: true,
      },
      where: {
        publisher_id: uid,
        id: articleID,
      },
    });
    if (!article)
      return WebResult.failure(undefined, "删除失败，该用户不存在该文章");

    try {
      // 删除归档（一个文章可能有多个归档，所有要deleteMany）
      // await this.prisma.article_archive.deleteMany({
      //   where: {
      //     article_id: articleID,
      //   },
      // });
      // 删除浏览量记录
      // await this.prisma.article_view.delete({
      //   where: {
      //     article_id: articleID,
      //   },
      // });
      // 删掉点赞数据
      // await this.prisma.article_like.deleteMany({
      //   where: {
      //     article_id: articleID,
      //   },
      // });

      // 删除文章
      await this.prisma.article.delete({
        where: {
          publisher_id: uid,
          id: articleID,
        },
      });
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "删除失败");
    }

    this.articleService.deleteArticleContentDataFile(uid, articleID);
    return WebResult.success(undefined, "删除成功");
  }
}
